package com.ttg.web.service.Impl;

import com.ttg.common.component.LoginCrypto;
import com.ttg.common.config.OrganConfig;
import com.ttg.common.constant.BasicConstant;
import com.ttg.common.dto.OrganEncryptDto;
import com.ttg.common.entities.BaseResult;
import com.ttg.common.enums.*;
import com.ttg.common.exception.BaseException;
import com.ttg.common.utils.*;
import com.ttg.common.dto.BasicDto;
import com.ttg.common.vo.OrganCheckSignVo;
import com.ttg.common.vo.OrganEncryptVo;
import com.ttg.model.pojo.*;
import com.ttg.web.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zhangHongYuan
 * @description
 * @date 2023/7/13 15:12
 */
@Slf4j
@Service
public class OpenAuthServiceImpl implements OpenAuthService {

    @Value("${rating.aes.key}")
    private String aesKey;

    @Value("${rating.open_id}")
    private String openId;

    @Value("${rating.rsa.privateKey}")
    private String privateKey;

    @Value("${rating.rsa.publicKey}")
    private String publicKey;

    @Autowired
    private OrganConfig organConfig;
    @Autowired
    private OrganTokenService organTokenService;
    @Autowired
    private AdminService adminService;
    @Autowired
    private OrganService organService;
    @Autowired
    private AgentService agentService;
    @Autowired
    private AdminRoleAuthService adminRoleAuthService;
    @Autowired
    private AdminAuthService adminAuthService;
    @Autowired
    private OrganOpenService organOpenService;
    @Autowired
    private ManageAuthService manageAuthService;

    @Override
    public boolean verifyRsaSign(BasicDto dto) {
        if (!openId.equals(dto.getOpen_id())){
            throw new BaseException(ErrorCodeEnums.REQUEST_AUTH_ERROR);
        }
        Map<String,String > map = new HashMap<>();
        map.put("timestamp", dto.getTimestamp());
        map.put("randomStr",dto.getRandomStr());
        map.put("data", dto.getData());
        String sign = dto.getSign();
        String sort = BasicUtils.sort(map);
        String s = BasicUtils.SHAL1(sort);
        boolean b = false;
        try {
            b = RsaUtils.verify(s.getBytes(StandardCharsets.UTF_8),publicKey,sign);
        } catch (Exception e) {
            throw new BaseException(ErrorCodeEnums.REQUEST_AUTH_ERROR);
        }
        return b;
    }
//    @Override
//    public OrganCheckSignVo verifyRsaSign(BasicDto dto) {
//        if (Objects.isNull(dto)) {
//            throw new BaseException(ErrorCodeEnums.APP_STATUS_ABNORMAL.getErrcode(), "token or open_id is null");
//        }
//        log.info("dto:{}", JsonUtils.objectToJson(dto));
//        if (StringUtils.isNotBlank(dto.getOpen_id())) {
//            return checkSignByOpenId(dto);
//        }else{
//            return manageAuthService.checkSign(dto);
//        }
//    }

    @Override
    public String getAesDataAndRsaSign(String data) {
        Map<String, String> map = new HashMap<>();
        BaseResult baseResult = JsonUtils.jsonToObject(data, BaseResult.class);
        map.put("errorcode",baseResult.getErrcode());
        map.put("msg", baseResult.getMsg());
        String errcode = baseResult.getErrcode();
        if (!ErrorCodeEnums.SUCCESS.getErrcode().equals(errcode)) {
            return JsonUtils.objectToJson(map);
        }
        Object data1 = baseResult.getData();
        String str =JsonUtils.objectToJson(data1);
        if (Objects.nonNull(data1)) {
            try {
                str = SecurityUtils.encrypt(str, aesKey);
            } catch (Exception e) {
                throw new BaseException(ErrorCodeEnums.ENCRYPT_ERROR);
            }
        }
        map.put("data", str);
        String sort = BasicUtils.sort(map);
        String s2 = BasicUtils.SHAL1(sort);
        String s1 = null;
        try {
            s1 = RsaUtils.rsaSignByPrivateKey(s2, privateKey);
        } catch (Exception e) {
            throw new BaseException(ErrorCodeEnums.SIGN_ERROR);
        }
        map.put("sign", s1);
        return JsonUtils.objectToJson(map);
    }

    @Override
    public String decryptAes(String data) {
        String decrypt = null;
        try {
            decrypt = SecurityUtils.decrypt(data, aesKey);
        } catch (Exception e) {
            throw new BaseException(ErrorCodeEnums.DECRYPT_ERROR);
        }
        return decrypt;
    }

    /**
     * openid 验签 以openid
     */
    private OrganCheckSignVo checkSignByOpenId(BasicDto organBaseDto) {
        String openId = organBaseDto.getOpen_id();
        OrganOpen organOpen = organOpenService.getByOpenIdAndStatus(openId, OrganOpenStatusEnum.TYPE_1);
        if (Objects.isNull(organOpen)) {
            throw new BaseException(ErrorCodeEnums.TOKEN_OVERDUE);
        }
        Organ organ = organService.getOrganByOrgNoAndStatus(organOpen.getOgoOrgNo(), OrgStatusEnum.NORMAL);
        if (Objects.isNull(organ)) {
            throw new BaseException(ErrorCodeEnums.TOKEN_OVERDUE);
        }
        Agent agent = agentService.getAgentByAgentNoAndOrgNo(organOpen.getOgoAgentNo(), organ.getOrgNo());
        if (Objects.nonNull(agent) && !AgentStatusEnum.CHECKED.getValue().equals(agent.getAgentStatus())) {
            throw new BaseException(ErrorCodeEnums.NOT_AUTH);
        }
        OrganCheckSignVo vo = new OrganCheckSignVo();
        vo.setOrgNo(organOpen.getOgoOrgNo());
        vo.setAgentNo(organOpen.getOgoAgentNo());
        vo.setAdmId(BasicConstant.LONG_0);
        vo.setTokenId(BasicConstant.LONG_0);
        vo.setAesKey(organOpen.getOgoOpenKey());
        if (StringUtils.isNotBlank(organOpen.getOgoAgentPublicKey())) {
            vo.setOrgPublicKey(organOpen.getOgoAgentPublicKey());
        } else {
            vo.setOrgPublicKey(organOpen.getOgoPublicKey());
        }
        vo.setOrgPrivateKey(organOpen.getOgoPrivateKey());
        vo.setOrgMaxLevel(organ.getOrgMaxLevel());
        if (Objects.nonNull(agent)) {
            vo.setAgentLevel(agent.getAgentLevel());
        }
        vo.setApiAuth(organOpen.getOgoType());
        String timestamp = organBaseDto.getTimestamp();
        if (StringUtils.isBlank(timestamp) || Math.abs(DateTimeUtil.currentTimeSeconds() - Long.parseLong(timestamp)) > 180) {
            throw new BaseException(ErrorCodeEnums.TOKEN_OVERDUE);
        }
        String randStr = organBaseDto.getRandStr();
        if (StringUtils.isBlank(randStr) || randStr.length() < 32) {
            throw new BaseException(ErrorCodeEnums.TOKEN_OVERDUE);
        }
        //验签
        boolean checkSign = verifySign(organBaseDto, organOpen.getOgoPublicKey());

        if (!checkSign) {
            log.info("验签失败organBaseDto=>{}", JsonUtils.objectToJson(organBaseDto));
            throw new BaseException(ErrorCodeEnums.SIGN_ERROR);
        }
        //解密数据
        String data = organBaseDto.getData();
        if (StringUtils.isNotBlank(data)) {
            try {
                //根据 sign_type 获取加解密工具对象
                LoginCrypto loginCrypto = JmSwitch.getLoginCrypto(organBaseDto.getSign_type());
                vo.setData(loginCrypto.decrypt(data, organOpen.getOgoOpenKey()));
            } catch (BaseException e) {
                throw e;
            } catch (Exception e) {
                log.error("系统异常: ", e);
                throw new BaseException(e.getMessage());
            }
        }
        vo.setRequestType(BasicConstant.OPENAPI_REQUEST_TYPE);
        vo.setUrl(organBaseDto.getUrl());
        return vo;
    }

    /**
     * Token方式登录验签
     */
    private OrganCheckSignVo checkSignByLogin(BasicDto organBaseDto) {
        //根据 sign_type 获取加解密工具对象
        LoginCrypto loginCrypto = JmSwitch.getLoginCrypto(organBaseDto.getSign_type());
        OrganCheckSignVo vo = new OrganCheckSignVo();
        String defaultAesKey = loginCrypto.getDefaultKey(CryptoKeyConfigTypeEnum.ORGAN);
        if (StringUtils.isBlank(defaultAesKey)) {
            throw new BaseException("系统默认aesKey未配置");
        }
        if (StringUtils.isBlank(organBaseDto.getData())) {
            throw new BaseException("请求参数为空");
        }
        if (StringUtils.isBlank(organBaseDto.getSign())) {
            throw new BaseException(ErrorCodeEnums.SIGN_NOT_NULL);
        }
        //验签
        boolean checkSign = loginCrypto.verifySign(organBaseDto);
        if (!checkSign) {
            log.error("验签失败：{}", JsonUtils.objectToJson(organBaseDto));
            throw new BaseException(ErrorCodeEnums.SIGN_ERROR);
        }
        //解密数据
        String data = organBaseDto.getData();
        if (StringUtils.isNotBlank(data)) {
            try {
                vo.setData(loginCrypto.decrypt(data, defaultAesKey));
            } catch (BaseException e) {
                throw e;
            } catch (Exception e) {
                log.error("系统异常: ", e);
                throw new BaseException(e.getMessage());
            }
        }
        vo.setAesKey(defaultAesKey);
        vo.setRequestType(BasicConstant.TOKEN_REQUEST_TYPE);
        vo.setUrl(organBaseDto.getUrl());
        return vo;
    }


    /**
     * token验签
     */
    private OrganCheckSignVo checkSignByToken(BasicDto organBaseDto) {
        String token = organBaseDto.getToken();
        OrganToken organToken = organTokenService.getOrganTokenByTokenAndStatus(token, OrganTokenStatusEnum.TYPE_1.getValue());
        if (Objects.isNull(organToken)) {
            throw new BaseException(ErrorCodeEnums.LOGIN_TOKEN_OVERDUE);
        }
        //访问时间超过30分钟则过期
        LocalDateTime tokenLastAccess = organToken.getTokenLastAccess();
        if (tokenLastAccess.plusMinutes(30).isBefore(LocalDateTime.now())) {
            OrganToken up = new OrganToken();
            up.setTokenId(organToken.getTokenId());
            up.setTokenStatus(OrganTokenStatusEnum.TYPE_2.getValue());
            organTokenService.updateById(up);
            throw new BaseException(ErrorCodeEnums.LOGIN_TOKEN_OVERDUE);
        }
        OrganToken up = new OrganToken();
        up.setTokenId(organToken.getTokenId());
        up.setTokenLastAccess(LocalDateTime.now());
        organTokenService.updateById(up);
        Admin admin = adminService.getById(organToken.getTokenAdmId());
        if (Objects.isNull(admin)) {
            throw new BaseException(ErrorCodeEnums.APP_STATUS_ABNORMAL);
        }

        OrganCheckSignVo vo = new OrganCheckSignVo();
        vo.setOrgNo(admin.getAdmOrgNo());
        vo.setAgentNo(admin.getAdmAgentNo());
        vo.setAdmId(admin.getAdmId());
        vo.setTokenId(organToken.getTokenId());
        vo.setAesKey(organToken.getTokenAesKey());
        vo.setRequestType(BasicConstant.TOKEN_REQUEST_TYPE);
        vo.setUrl(organBaseDto.getUrl());
        //只需要登录认证,不需要鉴权的url
        boolean isAuth = Optional.ofNullable(organConfig.getIgnoreAuthUrls()).orElse(new ArrayList<>()).stream().anyMatch(ignoreUrl -> ignoreUrl.equals(organBaseDto.getUrl()));
        if (!isAuth) {
            //校验权限
            List<Long> admRoleIds = Arrays.stream(admin.getAdmRoleId().split(",")).map(Long::valueOf).collect(Collectors.toList());
            List<AdminRoleAuth> adminRoleAuths = adminRoleAuthService.selectAdminRoleAuthByRoleIds(admRoleIds);
            if (CollectionUtils.isEmpty(adminRoleAuths)) {
                throw new BaseException(ErrorCodeEnums.NOT_AUTH);
            }
            Set<Long> idSet = new HashSet<>();
            for (AdminRoleAuth adminRoleAuth : adminRoleAuths) {
                Long araAuthId = adminRoleAuth.getAraAuthId();
                idSet.add(araAuthId);
            }
            List<AdminAuth> adminAuths = adminAuthService.listByIds(idSet);
            if (CollectionUtils.isEmpty(adminAuths)) {
                throw new BaseException(ErrorCodeEnums.NOT_AUTH);
            }
            Set<String> urlSet = new HashSet<>();
            for (AdminAuth adminAuth : adminAuths) {
                if (StringUtils.isNotBlank(adminAuth.getAuthUrl())) {
                    String[] split = adminAuth.getAuthUrl().split(",");
                    Collections.addAll(urlSet, split);
                }
            }
            if (!urlSet.contains(organBaseDto.getUrl())) {
                throw new BaseException(ErrorCodeEnums.NOT_AUTH);
            }
        }

        if (StringUtils.isNotBlank(organBaseDto.getData())) {
            LoginCrypto loginCrypto = JmSwitch.getLoginCrypto(organBaseDto.getSign_type());
            if (StringUtils.isBlank(organBaseDto.getSign())) {
                throw new BaseException(ErrorCodeEnums.SIGN_NOT_NULL);
            }
            //验签
            boolean checkSign = loginCrypto.verifySign(organBaseDto);
            if (!checkSign) {
                throw new BaseException(ErrorCodeEnums.SIGN_ERROR);
            }
            //解密数据
            String data = organBaseDto.getData();
            if (StringUtils.isNotBlank(data)) {
                try {
                    vo.setData(loginCrypto.decrypt(data, organToken.getTokenAesKey()));
                } catch (BaseException e) {
                    throw e;
                } catch (Exception e) {
                    log.error("系统异常:{} ", e.getMessage());
                    throw new BaseException(e.getMessage());
                }
            }
        }
        return vo;
    }

    /**
     * 验签
     */
    private boolean verifySign(BasicDto organBaseDto, String publicKeyStr) {
        LoginCrypto loginCrypto = JmSwitch.getLoginCrypto(organBaseDto.getSign_type());
        boolean signResult = false;
        try {
            TreeMap<String, String> verifyData = new TreeMap<>();
            String sign = organBaseDto.getSign();
            verifyData.put("open_id", organBaseDto.getOpen_id());
            if (StringUtils.isNotBlank(organBaseDto.getLang())) {
                verifyData.put("lang", organBaseDto.getLang());
            }
            verifyData.put("timestamp", organBaseDto.getTimestamp());
            verifyData.put("randStr", organBaseDto.getRandStr());
            if (StringUtils.isNotBlank(organBaseDto.getData())) {
                verifyData.put("data", organBaseDto.getData());
            }
            String sortedStr = BasicUtils.sort(verifyData);
            String shal1 = BasicUtils.SHAL1(sortedStr);
            // PublicKey publicKey = RsaUtils.ConvertPublicKey(publicKeyStr);
            // signResult = SecurityUtils.verifySignWithRsa(sign, shal1, publicKey);
            signResult = loginCrypto.verifyByPublicKey(sign, shal1, publicKeyStr);
        } catch (Exception e) {
            log.error("验签异常", e);
        }
        return signResult;
    }
}
